feat(ide): IntelliJ-style Java authoring — New → Java scaffolding + full Monaco Java IntelliSense#6076
feat(ide): IntelliJ-style Java authoring — New → Java scaffolding + full Monaco Java IntelliSense#6076iliyan-velichkov wants to merge 14 commits into
Conversation
…ull Monaco Java IntelliSense Make writing client Java in the browser IDE feel like IntelliJ. New → Java submenu (Projects view): - Package (creates the nested folder chain from a dotted name) - Class, Interface, Enum, Annotation, Record, Exception - Controller, Job, Listener, WebSocket, Repository — strong-interface skeletons A fully-qualified name (com.test.MyClass) creates the package folders and the matching package declaration; a simple name lands in the selected folder. Logic lives in a new pure helper js/java-new.js; projects.js orchestrates dialog → nested folder → file → open via the existing WorkspaceService. Java IntelliSense (editor-monaco LSP client): - Auto-import on completion (completionItem/resolve → additionalTextEdits) - Dirigible SDK suggestions ranked first (sortText bucketing) - Code actions: quick-fixes, organize imports, and generate constructor/getters/setters/toString/equals&hashCode with a member-picker dialog - Rename symbol, find references, document formatting - Server→client workspace/applyEdit + configuration handling - Format-on-save for Java reuses the existing TypeScript auto-format mechanism via the new formatting provider (same global toggle) Tests: - CreateJavaArtifactsIT drives New → Java end to end and asserts folders, files, package declarations and skeleton signals over the workspace REST API - Browser.hoverOnElementByAttributePatternAndText + Workbench.createJavaArtifact Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
…avaArtifactsIT pass
JDT.LS was launched with -Xmx512m, which OOMs ("Java heap space") while indexing
the full platform classpath (~1000+ entries), killing the language server and with
it completion / auto-import / code actions. Make the max heap configurable via
DIRIGIBLE_JAVA_LSP_MAX_HEAP (DirigibleConfig.JAVA_LSP_MAX_HEAP) and default it to 2g.
Test framework: CreateJavaArtifactsIT now drives the nested New → Java → <item>
context menu reliably. Replaced the strict hover/visibility approach (which cannot
reach a collapsed 3rd-level submenu in headless Chrome, where moveToElement does not
fire mouseenter) with Browser.clickCascadingMenuItem, which scopes to the open
context menu and drives its own Angular mouseenter/click handlers in a single
in-frame script. Verified: the IT passes end to end (package folders, package
declarations and strong-interface skeleton signals all asserted over the workspace
REST API).
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Follow-up while testing locallyJDT.LS heap fix. JDT.LS launched with Integration test now green. |
…ntity is an input - After creating a Java artefact the project tree now expands to and selects the new file (building/reusing the package folder nodes) instead of reloading the project node, which collapsed the structure. - The Repository skeleton no longer reuses the repository class name as its entity type parameter (DemoRepo → JavaRepository<DemoRepo>). The dialog now prompts for the entity type (simple or fully-qualified; a qualified name adds the import), producing e.g. JavaRepository<Country> with super(Country.class). - CreateJavaArtifactsIT + Workbench.createJavaArtifact updated for the entity prompt. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
…mpletion - Completion now propagates JDT.LS's isIncomplete so Monaco re-queries as you type (fixes the first Ctrl+Space showing nothing), and forwards the real trigger kind/character plus filterText/preselect/commitCharacters for correct filtering. - Registered a Monaco editor opener: Go to Definition / Find References to a symbol in another workspace file now opens that file in the IDE and reveals the line (the single-file editor previously had no model for other files, so navigation silently did nothing). Same-file targets still jump within the editor. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
… file Renaming a Java symbol (F2) now applies JDT.LS's full rename WorkspaceEdit across the whole workspace instead of only the current file: - Text edits in every referencing file are applied and persisted (read-modify-write over the workspace REST API, CSRF-guarded); the current file goes through the live Monaco model + save. - RenameFile operations are honoured, so renaming a public class/interface/enum also renames its .java file and updates all references; when that file is the one being edited, the editor tab switches to the new file. - Other open editors reload the changed files (dirty editors are skipped to avoid clobbering unsaved work) via a new monaco.file.reload topic. Falls back to current-file-only rename if the IDE persistence hook is unavailable. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
…s, semantic colors, CodeLens, override/implement, keywords Adds JDT.LS-backed language features to the Monaco Java editor: Navigation & structure: - Outline / breadcrumbs / sticky scroll (documentSymbol), code folding (foldingRange), occurrence highlight (documentHighlight), Go to Implementation and Go to Type Definition (reusing the cross-file editor opener), Format Selection (rangeFormatting) and Smart Expand Selection (selectionRange). Editor intelligence: - Inlay hints (parameter names + inferred types) and semantic-token highlighting (legend captured from the initialize result; semanticHighlighting enabled on the editor). - CodeLens with "N references / N implementations" (click opens the peek). - Override/Implement Methods wired into the existing member-picker generate flow. - Always-available Java keyword completion, ranked below SDK/LSP results. Advertises the matching client capabilities and enables the JDT.LS inlay-hint and references/implementations CodeLens settings. All are additive providers; the exact JDT.LS command ids are guarded. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
…yword colors - Find/Peek References (and go-to-definition/implementation/type-definition) now create in-memory models for the referenced files, so the peek shows a real code preview instead of only file/line/column. - Flush the pending debounced didChange before a completion request, so JDT.LS sees the just-typed text on the first Ctrl+Space (previously the first invocation completed against stale content and only the second worked). - Revert the forced semanticHighlighting flag: JDT.LS emits keyword/modifier semantic tokens the vs-dark-based themes don't style, which left Java keywords uncolored. The semantic-tokens provider stays registered (inert until a theme opts in) so basic Monarch keyword coloring is restored. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
…e after rename Root cause of "no quick-fixes / no Implement Methods / Generate toString does nothing": - We advertised the *PromptSupport extended capabilities, so JDT.LS returned source actions (generate toString/constructors/accessors, override/implement, organize imports) as client-side "java.action.*Prompt" commands that the vscode-java extension implements but we don't — and the server commands we tried to call (checkToStringStatus/generateToString/…) don't exist on JDT.LS. Dropping those flags makes JDT.LS return the same actions as resolvable WorkspaceEdits (all members), which applyCodeAction resolves and applies directly. - Code-action requests sent diagnostics reconstructed from Monaco markers, which lose the LSP code/data JDT.LS matches quick-fixes against. We now keep the original published diagnostics per file and send the ones overlapping the request range, so "Add unimplemented methods", "Create field", etc. appear. Also: the Projects view ignored file rename/move events, so after a class rename the tree still showed the old file. It now reloads on platform.files.renamed/moved. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
…n completion) Renaming a public type (F2) had two defects: - When JDT.LS ordered the RenameFile before the text edit, the edit was keyed by the new URI; we wrote the old content (old type name) into the new file, producing "The public type X must be defined in its own file". Edits are now re-attributed to the on-disk (old) URI before applying, so the new file gets the new type name. - After renaming on disk we never told JDT.LS, so it kept validating stale state until a page refresh and kept suggesting the old type name in completion. We now send textDocument/didClose for the renamed-away file and workspace/didChangeWatchedFiles (Deleted old / Created new / Changed edited) so the server re-indexes immediately — clearing the stale diagnostic and dropping the old name from completion. This also removes the exception/fallback path that made the first Enter occasionally apply only the in-editor edit without renaming the file. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
- Cap JDT.LS completion at maxResults=50 so import/type suggestions over the large platform classpath return faster. - On a Java file rename, the Projects view now renames the matching tree node in place and selects it (keeping it revealed) instead of doing a full collapse-reload, so the renamed file stays visible and highlighted; falls back to a reload if the node isn't loaded. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
…meter to field) Set the Monaco code-action lightbulb to "on" so it appears whenever any code action is available at the cursor — not only on diagnostics. This surfaces JDT.LS refactor assists like "Assign parameter to new field" / "Assign all parameters to new fields" (kind refactor.assign.field) on a constructor parameter, plus Extract Method/Variable/ Constant/Field, as a visible suggestion instead of being hidden behind Refactor…. The assist itself already flowed through the existing code-action provider. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
…ce CodeLens The Java editor had become slow to "enable" features on a fresh project and sluggish while typing. Two avoidable load sources, on top of JDT.LS's inherent cold-start indexing of the large platform classpath: - Code-action requests omitted triggerKind, so JDT.LS computed the full set of quick assists/refactorings on every passive-lightbulb evaluation (every cursor move). Now we forward Monaco's trigger (Invoke=1/Auto=2, 1:1 with the LSP kind): the passive lightbulb computes only quick-fixes (cheap); full assists/refactorings only on explicit Ctrl+. / Refactor…. - references/implementations CodeLens ran a reference search for every declaration on open and on every edit — the heaviest always-on provider. Disabled by default. - Reverted the eager `lightbulb: 'on'` (its purpose was to surface refactor assists in the passive bulb, which we instead keep on the explicit Refactor… surface). Refactor assists like "Assign parameter to new field" / Extract remain available via Refactor… (Ctrl+Shift+R). Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
…ling / CreateNewFileIT) BlimpKit's context menu disables scrolling for any submenu that contains a nested submenu (contextmenu.js canScroll). Nesting the "Java" group inside "New" therefore turned off scrolling for the whole New menu, so once enough artefact templates are registered the menu overflows the viewport and top items (e.g. "JavaScript Service") become unreachable — which failed CreateNewFileIT. Move "Java" to a top-level context-menu entry (sibling of New) on project/folder nodes. New keeps its scrolling; "Java" is its own 2-level submenu. Workbench page object navigates Java → <item> accordingly. Verified: CreateNewFileIT and CreateJavaArtifactsIT both pass. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
…ath; index freshness The Java editor was slow to "enable" features after every jar restart because JDT.LS re-indexed the whole platform classpath each time. Causes and fixes: - JdtLsManager wiped the JDT.LS data/index dir on every start. Since ClassPathIndex now extracts to a stable cache dir, the index only goes stale when the classpath actually changes. Wipe is now guarded by a classpath fingerprint (SHA-256 of the sorted classpath entries) stored next to the data dir: matching fingerprint → reuse the index (warm, fast restart); mismatch/first run → rebuild once. - Pre-warm the (cached) compile classpath on the boot virtual thread so the first Java file open doesn't pay the multi-second materialisation. - After didOpen, send workspace/didChangeWatchedFiles(Created) so JDT.LS's project model includes just-created files immediately (a fresh interface is now offered in a sibling class without waiting for a rebuild). - Dropped java.completion.maxResults (it equalled JDT.LS's default and only risked trimming relevant results such as the project's own types). Verified: CreateJavaArtifactsIT + CreateNewFileIT pass; logs show "Pre-warmed compile classpath" and the fingerprint-guarded "Building/Reusing JDT.LS index". Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
| logger.info("[java-lsp] Reusing cached JDT.LS index for {}/{}", sanitize(username), sanitize(workspace)); | ||
| } else { | ||
| logger.info("[java-lsp] Building JDT.LS index for {}/{} (first run or classpath changed)", sanitize(username), | ||
| sanitize(workspace)); |
| if (indexValid) { | ||
| logger.info("[java-lsp] Reusing cached JDT.LS index for {}/{}", sanitize(username), sanitize(workspace)); | ||
| } else { | ||
| logger.info("[java-lsp] Building JDT.LS index for {}/{} (first run or classpath changed)", sanitize(username), |
| boolean indexValid = !fingerprint.isEmpty() && Files.isDirectory(dataDir) && Files.exists(fingerprintMarker) | ||
| && fingerprint.equals(readFingerprint(fingerprintMarker)); | ||
| if (indexValid) { | ||
| logger.info("[java-lsp] Reusing cached JDT.LS index for {}/{}", sanitize(username), sanitize(workspace)); |
| boolean indexValid = !fingerprint.isEmpty() && Files.isDirectory(dataDir) && Files.exists(fingerprintMarker) | ||
| && fingerprint.equals(readFingerprint(fingerprintMarker)); | ||
| if (indexValid) { | ||
| logger.info("[java-lsp] Reusing cached JDT.LS index for {}/{}", sanitize(username), sanitize(workspace)); |
What & why
Authoring client Java in the browser IDE is currently bare: the only Java entry in the workbench New menu is a single "Java Service" template, and the Monaco editor wires JDT.LS for read-only features (completion, hover, diagnostics). This PR makes Java development in the web IDE feel like IntelliJ — fast, strong-interface scaffolding plus a real completion/generation/refactoring layer.
1. New → Java submenu (Projects view)
Right-click a project or folder → New → Java:
com.test.iliyan, the full nested folder chain is created.@Controller+@Get,implements JobHandler,implements MessageHandler,implements WebsocketHandler,@Repository extends JavaRepository<T>).Naming follows IntelliJ: a fully-qualified name (
com.test.MyClass) creates the package folders and emits the matchingpackagedeclaration (mirroring the file's path relative to the project root, as the client-Java compiler requires); a simple name lands in the selected folder with no package line.All Java-specific logic (name parsing/validation + skeleton generation) lives in a new pure, dependency-free helper
view-projects/js/java-new.js;projects.jsonly orchestrates dialog → nested folder → file → open through the existingWorkspaceService(folder creation relies on the server'sforceMkdirto create parents).2. Java IntelliSense (editor-monaco LSP client)
Extends
editor-monaco/js/lsp/java-lsp-client.ts(rebuilt bundle committed):completionItem/resolvepullsadditionalTextEdits, so accepting a type inserts itsimport.org.eclipse.dirigible.sdk.*rank above everything else.toString/equals&hashCodewith a member-picker dialog (BlimpKitDialogHub).workspace/applyEdit+workspace/configurationhandling so command-driven edits land.initializecapabilities are expanded (codeAction literal/resolve, completion resolve, rename prepare, references, formatting, executeCommand, applyEdit) since JDT.LS gates features on them.3. Format on save for Java
Registering the Java document-formatting provider enables the editor's existing TypeScript format-on-save path for Java automatically — same global "Auto-Formatting" toggle and "Toggle Auto-Formatting" action, no Java-specific branch.
Tests
CreateJavaArtifactsITdrives New → Java end to end for the base types, the skeletons, a fully-qualified class and a nested package, asserting the created folders, files,packagedeclarations and skeleton signals over the workspace REST API (deterministic — no Monaco DOM scraping).Browser.hoverOnElementByAttributePatternAndText(for cascading submenu navigation) +Workbench.createJavaArtifact.Verification
mvn -P quick-build installofview-projects+editor-monaco(the Maven esbuild step reproduces the committedjava-lsp-client.jsbyte-for-byte),tsc --noEmitclean.java-new.jsskeleton/parse logic checked under Node; framework + IT modules compile.CreateJavaArtifactsITUI flow runs under the matched Chrome/ChromeDriver in CI.🤖 Generated with Claude Code